#ifndef INSN_H
#define INSN_H

#include "uarch/types.h"

/** Much of the types are derived from SESC.  */
typedef enum {
  // Main instruction opcode
  OpMask   = 0xF00, // Mask for the main instruction opcode
  OpNop    = 0x000,
  OpInt    = 0x100,
  OpFp     = 0x200,
  OpBr     = 0x300,
  OpMem    = 0x400,

  OpSubMask  = 0xFF0, // Mask for the subtype
  // Subtypes for integer opcodes
  OpIntALU    = 0x00 + OpInt, // ALU operation
  OpIntMul    = 0x10 + OpInt, // Multiply
  OpIntDiv    = 0x20 + OpInt, // Divide
  // Subtypes for FP opcodes
  OpFpALU     = 0x00 + OpFp,  // "Easy" FP op
  OpFpMul     = 0x10 + OpFp,  // Multiply-like FP op
  OpFpDiv     = 0x20 + OpFp,  // Divide-like FP op (also sqrt, recip, etc)
  // Subtypes for Branch opcodes
  OpBrJump    = 0x00 + OpBr,  // Unconditional branch
  OpBrCond    = 0x10 + OpBr,  // Conditional branch
  OpBrCall    = 0x20 + OpBr,  // Function call
  OpBrCCall   = 0x30 + OpBr,  // Conditional function call
  OpBrRet     = 0x40 + OpBr,  // Function return
  OpBrCRet    = 0x50 + OpBr,  // Conditional function return
  OpBrTrap    = 0x80 + OpBr,  // Trap/syscall
  OpBrCTrap   = 0x90 + OpBr,  // Conditional trap/syscall
  // Subtypes for memory opcodes
  OpMemLd    = 0x00 + OpMem, // Load
  OpMemSt    = 0x10 + OpMem, // Store
  OpSynLd    = 0x20 + OpMem, // Sync Load
  OpSynSt    = 0x30 + OpMem, // Sync Store
  OpSync     = 0x40 + OpMem, // Sync
  MemSizMask  = 0xF, // Mask for load/store access size
  OpMemLd1    = OpMemLd+1,
  OpMemLd2    = OpMemLd+2,
  OpMemLd4    = OpMemLd+4,
  OpMemLd8    = OpMemLd+8,
  OpMemSt1    = OpMemSt+1,
  OpMemSt2    = OpMemSt+2,
  OpMemSt4    = OpMemSt+4,
  OpMemSt8    = OpMemSt+8,
  OpMemLl4    = OpSynLd+4,
  OpMemSc4    = OpSynSt+4,
  OpMemLl8    = OpSynLd+8,
  OpMemSc8    = OpSynSt+8
} opcode_t ;

class Insn {
public:
    opcode_t opcode;
    paddr_t  addr;
    uint64_t imm;
    uint8_t  dest;
    uint8_t  src1;
    uint8_t  src2;
    
public:
    Insn (opcode_t opcode = OpNop, paddr_t addr = 0x0,
          uint8_t dest = 0, uint8_t src1 = 0,  uint8_t src2 = 0, uint64_t imm = 0);
    ~Insn (void);

    bool is_memop (void);
    Mreq *create_mreq (void);

    void dump (void);
};

#endif // INSN_H
